package ssm.ann.controller;

import lombok.*;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@RestController
@RequestMapping
// https://blog.csdn.net/f641385712/article/details/98378045
@SessionAttributes(names = {"name", "age"}, types = My_09_Attribute.Person.class)
public class My_09_Attribute {
    /**
     * ModelAttribute 缺省处理的是 Request 请求域，Spring MVC 还提供了 @SessionAttributes 来处理和 Session 域相关的模型数据
     */
    @ModelAttribute("myPersonAttr")
    public Person personModelAttr(HttpServletRequest request) {
        request.setAttribute("myApplicationName", "mvc-application");
        request.getSession().setAttribute("mySessionName", "session-application");
        return new Person("非功能方法", 50);
    }

    /**
     * /modelAttribute1?name=wo&age=10
     * 可以看到入参的 Person 对象即使没有标注 @ModelAttribute 也是能够正常被封装进值的（并且还放进了 ModelMap 里）。
     * 没有注解也会使用空构造创建，再使用 ServletRequestDataBinder.bind(ServletRequest request) 完成数据绑定（还可以 @Valid 校验）
     * https://blog.csdn.net/f641385712/article/details/98493425
     * https://blog.csdn.net/f641385712/article/details/98260361
     *
     * @ModelAttribute 标注在本方法上只会对本控制器有效。若使用在 @ControllerAdvice 组件上，它将是全局的。（当然可以指定 basePackages 来限制它的作用范围）
     */
    @GetMapping("/modelAttribute1")
    public void modelAttribute(Person person, ModelMap modelMap) {
        System.out.println(modelMap.get("person")); // 若上面注解没有指定 value 值，就是类名首字母小写
        System.out.println(modelMap.get("myPersonAttr"));
    }

    @GetMapping("/modelAttribute2")
    public void modelAttribute2(@ModelAttribute("myPersonAttr") Person person, ModelMap modelMap) {
        System.out.println(modelMap.get("person")); // 若上面注解没有指定 value 值，就是类名首字母小写
        System.out.println(modelMap.get("myPersonAttr"));
    }

    // ================================================================================================================

    @GetMapping("/sessionAttributes")
    public void sessionAttributes(HttpSession httpSession, ModelMap modelMap) {
        System.out.println(modelMap.get("myPersonAttr"));
        System.out.println(httpSession.getAttribute("myPersonAttr"));
    }

    // ================================================================================================================

    /**
     * @RequestAttribute 只负责从 request 里面取属性值，至于你什么时候往里放值，是有多种方式的可以达到的：
     * 只能使用在方法入参上。作用：从request中取对应的属性值
     * * @ModelAttribute 注解预存
     * * HandlerInterceptor拦截器中预存
     * * 请求转发带过来
     * @see RequestAttributeMethodArgumentResolver
     * https://blog.csdn.net/f641385712/article/details/98508272
     */
    @GetMapping("/requestAttribute1")
    public void requestAttribute1(@RequestAttribute("myApplicationName") String myApplicationName, HttpServletRequest request,
                                  ModelMap modelMap, @SessionAttribute("mySessionName") String mySessionName) {
        System.out.println(myApplicationName); // mvc-application
        System.out.println(mySessionName); // session-application
        // 从 request 里获取
        System.out.println(request.getAttribute("myApplicationName")); // mvc-application
        // 从 session 里获取
        System.out.println(request.getSession().getAttribute("myApplicationName")); // session-application
        // 从 model 里获取
        System.out.println(modelMap.get("myApplicationName")); // null 获取不到 attr 属性的
        System.out.println(modelMap.get("myPersonAttr")); // Person(name=非功能方法, age=50)
    }

    @GetMapping("/requestAttribute2")
    public void requestAttribute2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("myApplicationName", "fsx-application");
        request.getRequestDispatcher("/requestAttribute1").forward(request, response);
    }

    // ================================================================================================================

    @Getter
    @Setter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    static class Person {
        private String name;
        private Integer age;
    }
}
